package vader.redisMode;

import cn.hutool.core.convert.Convert;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @ Description   :  redis模板
 * @ Author        :  yongxin@mail.thefancyapp.com
 * @ CreateDate    :  2021/3/11 20:44
 * @ Version       :  1.0
 */
@Component
@ConditionalOnClass(RedisTemplate.class)
public class RedisTempleMode {

    @Autowired
    private RedisUtil redisUtil;
    /**
     * String类型的处理
     * @param key
     * @param expire null 默认保留到当天00:00:00
     * @param claszz
     * @param redisLoadable
     * @param <T>
     * @return
     */
    public <T> T findStringByKey(String key,Long expire,
                                          TypeReference<T> claszz, RedisLoadable<T> redisLoadable){
        //1.查询缓存，获取结果
        String jsonStr = String.valueOf(redisUtil.get(key));
        //2.如果缓存有数据，直接返回
        if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
            return JSON.parseObject(jsonStr,claszz);
        }else{
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this){
                jsonStr = String.valueOf(redisUtil.get(key));
                //2.如果缓存中存在数据，返回
                if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
                    return JSON.parseObject(jsonStr,claszz);
                }
                //核心业务 由具体的实现类来实现
                T result = redisLoadable.load();
                if(result != null){
                    //把从DB中获取的数据，设置到缓存中
                    if(expire == null){
                        expire = RedisUtil.everyDayExpire();
                    }
                    redisUtil.set(key,JSON.toJSONString(result),expire);
                }
                return result;
            }
        }
    }
    /**
     * 根据key 获取结果 --功能包含 double check
     * 先从redis中获取，redis中获取不到，再从DB中获取数据
     * @param key
     * @param expire
     * @param claszz
     * @param redisLoadable
     * @param <T>
     * @return
     */
    public <T> T findHashMapByKeyAndField(String key, String field,long expire,
                             TypeReference<T> claszz, RedisLoadable<T> redisLoadable){
        //1.查询缓存，获取结果
        String jsonStr = String.valueOf(redisUtil.hget(key,field));
        //2.如果缓存有数据，直接返回
        if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
            return JSON.parseObject(jsonStr,claszz);
        }else{
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this){
                jsonStr = String.valueOf(redisUtil.hget(key,field));
                //2.如果缓存中存在数据，返回
                if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
                    return JSON.parseObject(jsonStr, claszz);
                }
                //核心业务 由具体的实现类来实现
                T result = redisLoadable.load();
                if(result != null){
                    //把从DB中获取的数据，设置到缓存中
                    redisUtil.hset(key,field,result,expire);
                }
                return result;
            }
        }
    }

    /**
     * 根据key 获取结果 --功能包含 double check
     * 先从redis中获取，redis中获取不到，再从DB中获取数
     * 比较特殊 会获取fields中所以的值。所以的值都为NUll时，才从数据库获取
     *
     * @param key
     * @param fields
     * @param expire
     * @param claszz
     * @param redisLoadable
     * @param <T>
     * @return
     */
    public <T> Map<String, T> findHashMapByKeyAndFields(String key, List<String> fields, long expire,
                                           TypeReference<T> claszz, RedisLoadable<Map<String, T>> redisLoadable){
        //1.查询缓存，获取结果
        boolean result = true;
        Map<String, T> resultMap = new ConcurrentHashMap<>();
        for(String field :fields ){
            String jsonStr = String.valueOf(redisUtil.hget(key,field));
            if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
                resultMap.put(field,JSON.parseObject(jsonStr,claszz));
            }else{
                result =  false;
            }
        }
        //2.如果缓存有数据，直接返回
        if(result){
            return resultMap;
        }else{
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this){
                resultMap = new ConcurrentHashMap<>();
                for(String field :fields ){
                    String jsonStr = String.valueOf(redisUtil.hget(key,field));
                    if(StringUtils.isNotEmpty(jsonStr) && !"null".equalsIgnoreCase(jsonStr)){
                        resultMap.put(field,JSON.parseObject(jsonStr,claszz));
                    }else{
                        result =  false;
                    }
                }
                //2.如果缓存中存在数据，返回
                if(result){
                    return resultMap;
                }
                //核心业务 由具体的实现类来实现
                Map<String, T> businessResult = redisLoadable.load();
                //把从DB中获取的数据，设置到缓存中
                for (Map.Entry<String, T> temp : businessResult.entrySet()) {
                    redisUtil.hset(key,temp.getKey(),temp.getValue(),expire);
                }
                return businessResult;
            }
        }
    }

    /**
     *
     * @param key
     * @param expire
     * @param redisLoadable
     * @return
     */
    public Map<Object, Object> findMapsByKey(String key,Long expire, RedisLoadable<Map<Object, Object>> redisLoadable){
        //1.查询缓存，获取结果
        Map<Object, Object> mapValues = redisUtil.hmget(key);
        //2.如果缓存有数据，直接返回
        if(!mapValues.isEmpty()){
            return mapValues;
        }else{
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this){
                mapValues = redisUtil.hmget(key);
                //2.如果缓存中存在数据，返回
                if(!mapValues.isEmpty()) {
                    return mapValues;
                }
                //核心业务 由具体的实现类来实现
                Map<Object, Object> result = redisLoadable.load();
                if(result != null){
                    //把从DB中获取的数据，设置到缓存中
                    if (expire == null){
                        redisUtil.hmset(key, result);
                    }else{
                        redisUtil.hmset(key, result,expire);
                    }
                }
                return result;
            }
        }
    }

    /**
     * @param key
     * @param expire
     * @param redisLoadable
     * @return
     */
    public <T> T findHashMapByKey(String key, Long expire,
                                  cn.hutool.core.lang.TypeReference<T> clazz, RedisLoadable<T> redisLoadable) {
        //1.查询缓存，获取结果
        Map<Object, Object> mapValues = redisUtil.hmget(key);
        //2.如果缓存有数据，直接返回
        if (!mapValues.isEmpty()) {
            return Convert.convert(clazz, mapValues);
        } else {
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this) {
                mapValues = redisUtil.hmget(key);
                //2.如果缓存中存在数据，返回
                if (!mapValues.isEmpty()) {
                    return Convert.convert(clazz, mapValues);
                }
                //核心业务 由具体的实现类来实现
                T result = redisLoadable.load();
                if (result != null) {
                    //把从DB中获取的数据，设置到缓存中
                    Map<Object, Object> resultMap = Convert.convert(new cn.hutool.core.lang.TypeReference<Map<Object, Object>>() {
                    }, result);
                    if (expire == null) {
                        redisUtil.hmset(key, resultMap);
                    } else {
                        redisUtil.hmset(key, resultMap, expire);
                    }
                }
                return result;
            }
        }
    }

    /**
     * @param key
     * @param expire
     * @param redisLoadable
     * @return
     */
    public <T> T findHashMapByKey(String key, Long expire,
                                  Class<T> clazz, RedisLoadable<T> redisLoadable) {
        //1.查询缓存，获取结果
        Map<Object, Object> mapValues = redisUtil.hmget(key);
        //2.如果缓存有数据，直接返回
        if (!mapValues.isEmpty()) {
            return Convert.convert(clazz, mapValues);
        } else {
            //3.如果缓存没有数据，从DB获取，并且更新缓存
            synchronized (this) {
                mapValues = redisUtil.hmget(key);
                //2.如果缓存中存在数据，返回
                if (!mapValues.isEmpty()) {
                    return Convert.convert(clazz, mapValues);
                }
                //核心业务 由具体的实现类来实现
                T result = redisLoadable.load();
                if (result != null) {
                    //把从DB中获取的数据，设置到缓存中
                    Map<Object, Object> resultMap = Convert.convert(new cn.hutool.core.lang.TypeReference<Map<Object, Object>>() {
                    }, result);
                    if (expire == null) {
                        redisUtil.hmset(key, resultMap);
                    } else {
                        redisUtil.hmset(key, resultMap, expire);
                    }
                }
                return result;
            }
        }
    }

}
